//你好
const { def, base, intField, setEvent, regFieldGetSet, regEvent } = require("./jsdemo_windows.common.cpp.js");

const window_fields = ["left", "top", "width", "height", "fg_color", "bg_color"];
const window_event = ["beforeRender", "afterRender", "onMouseDown", "onMouseUp", "onMouseMove", "onKeyEvent"];

const window_def = String.raw/* cpp */`
class Window{

  public:
  Window();
  virtual ~Window();

  virtual void render(int mask);//0xFFFF 表示由windows触发，0 表示由window触发 是否触发JS回调事件
  virtual void mouseDown(int x, int y, int e);
  virtual void mouseUp(int x, int y, int e);
  virtual void mouseMove(int x, int y, int e);
  virtual void keyEvent(int key, int chr);
  

  FILE* getStream(int x, int y, int w, int h);// const char* str,
  void paintWin(int, int, int, int, int, int);
  void drawImgWin(void*, int, int, int, int);

  void jsCall(JSValue func);

  int printX;
  int printY;
  int printBaseX;
  int printBaseY;
  int printWidth;
  int printHeight;
  
  int printColor;
  int transparentColor;//TODO

  int baseX;
  int baseY;
  int abs_pos_x;
  int abs_pos_y;
  int needRefresh;
  FILE stream;
  Windows* windows;
  virtual void setWindows(Windows* windows);
  JSValue js_obj;
  void* tag;
  int id;

${window_fields.map((field)=>`
      int ${field};
`).join("")}


${window_event.map((event)=>`
  JSValue ${event};
`).join("")}

};
`;

const window = String.raw/* cpp */`
  Window::Window(){
    left = 0;
    top = 0;
    width = 0;
    height = 0;
    fg_color = 0;
    bg_color = 0xC0C0C0;
    windows = 0;
    needRefresh = 1;
    baseX = 0;
    baseY = 0;
    
${window_event.map((event)=> `
      ${event}.tag = JS_TAG_UNDEFINED;
`).join("")}

  }

  Window::~Window(){
    printf("~Window\n");
${window_event.map((event)=>`
    JS_FreeValue(ctx, ${event});
`).join("")}

  }

  void Window::jsCall(JSValue func){
    JSValue ret = JS_Call(ctx, func, js_obj, 0, 0);
    if (JS_IsException(ret)) {
      dump_exception(ctx, JS_GetException(ctx));
    }
    JS_FreeValue(ctx, ret);
  }

  void Window::render(int mask){
    if((needRefresh)){
      if((beforeRender.tag & mask)!=JS_TAG_UNDEFINED){
        jsCall(beforeRender);
      }
        
      abs_pos_x = baseX + left;
      abs_pos_y = baseY + top;

      if((afterRender.tag & mask)!=JS_TAG_UNDEFINED){
        jsCall(afterRender);
      }
      if(mask){
        needRefresh = 0;
      }
    }
  }

  void Window::paintWin(int x, int y, int w, int h, int color, int pattern){
    paint_pattern(abs_pos_x+x, abs_pos_y+y, w, h, color, pattern);
  }

  void Window::drawImgWin(void* srcAddr, int x, int y, int w, int h){
    drawImg(srcAddr, abs_pos_x+x, abs_pos_y+y, w, h);
  }

  FILE* Window::getStream(int x, int y, int w, int h){
    printX = x;
    printY = y;
    printBaseX = x;
    printBaseY = y;
    printWidth = w;
    printHeight = h;
    int color = getRGB565(fg_color);
    int bg = getRGB565(bg_color);
    printColor = (color<<16) | bg;

    memset((void*)&stream, 0, sizeof(FILE));
    stream.__magic = _IOMAGIC;
    stream.__mode.__write = 1;
    stream.__cookie = this;
    stream.__room_funcs = __default_room_functions;
    stream.__io_funcs.__write = writeWin;
    stream.__seen = 1;
    stream.__userbuf = 1;

    return &stream;
  }

  void Window::mouseDown(int x, int y, int e){
    if(onMouseDown.tag!=JS_TAG_UNDEFINED){
      JSValue args[3];
      args[0] = JS_NewInt32(ctx, x);
      args[1] = JS_NewInt32(ctx, y);
      args[2] = JS_NewInt32(ctx, e);
      JSValue ret = JS_Call(ctx, onMouseDown, js_obj, 3, args);
      if (JS_IsException(ret)) {
          dump_exception(ctx, JS_GetException(ctx));
      }
      JS_FreeValue(ctx, ret);
      JS_FreeValue(ctx, args[0]);
      JS_FreeValue(ctx, args[1]);
      JS_FreeValue(ctx, args[2]);
    }
  }
  void Window::mouseUp(int x, int y, int e){
    if(onMouseUp.tag!=JS_TAG_UNDEFINED){
      JSValue args[3];
      args[0] = JS_NewInt32(ctx, x);
      args[1] = JS_NewInt32(ctx, y);
      args[2] = JS_NewInt32(ctx, e);
      JSValue ret = JS_Call(ctx, onMouseUp, js_obj, 3, args);
      if (JS_IsException(ret)) {
          dump_exception(ctx, JS_GetException(ctx));
      }
      JS_FreeValue(ctx, ret);
      JS_FreeValue(ctx, args[0]);
      JS_FreeValue(ctx, args[1]);
      JS_FreeValue(ctx, args[2]);
    }
  }
  void Window::mouseMove(int x, int y, int e){
    if(onMouseMove.tag!=JS_TAG_UNDEFINED){
      JSValue args[3];
      args[0] = JS_NewInt32(ctx, x);
      args[1] = JS_NewInt32(ctx, y);
      args[2] = JS_NewInt32(ctx, e);
      JSValue ret = JS_Call(ctx, onMouseMove, js_obj, 3, args);
      if (JS_IsException(ret)) {
          dump_exception(ctx, JS_GetException(ctx));
      }
      JS_FreeValue(ctx, ret);
      JS_FreeValue(ctx, args[0]);
      JS_FreeValue(ctx, args[1]);
      JS_FreeValue(ctx, args[2]);
    }
  }
  void Window::keyEvent(int key, int chr){
    if(onKeyEvent.tag!=JS_TAG_UNDEFINED){
      JSValue args[2];
      args[0] = JS_NewInt32(ctx, key);
      args[1] = JS_NewInt32(ctx, chr);
      JSValue ret = JS_Call(ctx, onKeyEvent, js_obj, 2, args);
      if (JS_IsException(ret)) {
          dump_exception(ctx, JS_GetException(ctx));
      }
      JS_FreeValue(ctx, ret);
      JS_FreeValue(ctx, args[0]);
      JS_FreeValue(ctx, args[1]);
    }
  }
  void Window::setWindows(Windows* windows){
    this->windows = windows;
  }
  
  static void getIntVal(JSValue val, char* field, int* p){
    JSValue obj = JS_GetPropertyStr(ctx, val, field);
    if(obj.tag!=3){
      int v = 0;
      if(!JS_ToInt32(ctx, &v, obj)){
        *p = v;
      }
    }
    JS_FreeValue(ctx, obj);
  }

  static JSValue js_window_ctor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) {
    Window* p = new Window();
    JSValue obj = JS_NewObjectClass(ctx, js_window_class_id);
    p->js_obj = obj;
    JS_SetOpaque(obj, p);
    return obj;
  }
  static void js_window_finalizer(JSRuntime *rt, JSValue val) {
    Window *p = (Window*)JS_GetOpaque(val);
    if(p){
      delete p;
    }
  }

  ${window_fields.map((field)=>{
    return intField("Window", "window", field);
  }).join("")}
  
  ${window_event.map((event)=>{
    return setEvent("Window", "window", event);
  }).join("")}
  
  

  static JSValue js_window_paint(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Window *window = (Window*)JS_GetOpaque(this_val);
    if(argc==5){
      int x;
      JS_ToInt32(ctx, &x, argv[0]);
      int y;
      JS_ToInt32(ctx, &y, argv[1]);
      int w;
      JS_ToInt32(ctx, &w, argv[2]);
      int h;
      JS_ToInt32(ctx, &h, argv[3]);

      if(JS_IsNumber(argv[4])){
        int pattern;
        JS_ToInt32(ctx, &pattern, argv[4]);
        int color = getRGB565(window->fg_color);
        int bg = getRGB565(window->bg_color);
        int printColor = (color<<16) | bg;
        window->paintWin(x, y, w, h, printColor, pattern);
      }else{
        size_t size;
        void* buff = getBuffer(argv[4], &size);

        //IOWR(VGA_BLT, BLT_S_SWITCH, SWITCH_TRANS_ON_05 | transparentColor);//transparent on
        IOWR(VGA_BLT, BLT_S_SWITCH, SWITCH_TRANS_OFF_04);//transparent off
      
        //if(size == w*h*2){
        window->drawImgWin(buff, x, y, w, h);
        //}
      }


    }
  //   x
  //   y
  //   w
  //   h
  //   pattern/buffer

    
  // void Window::paintWin(int x, int y, int w, int h, int color, int pattern){
  //   paint_pattern(abs_pos_x+x, abs_pos_y+y, w, h, color, pattern);
  // }

  // void Window::drawImgWin(void* srcAddr, int x, int y, int w, int h){
  //   drawImg(srcAddr, abs_pos_x+x, abs_pos_y+y, w, h);
  // }



    // if(argc>=1){
    //   int left = 5;
    //   int top = 5;
    //   int width = window->width - 10;
    //   int height = window->height;
    //   if(JS_IsString(argv[0])) {
    //     char *str = (char*)JS_ToCString(ctx, argv[0]);
    //     if (str) {
    //       FILE* s = window->getStream(left, top, width, height);
    //       fprintf(s, str);
    //     }
    //     JS_FreeCString(ctx, str);
    //   }else{

    //   }
    // }
      
    return JS_UNDEFINED;
  }
  
  static JSValue js_window_print(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Window *window = (Window*)JS_GetOpaque(this_val);
    if(argc>=1){
      int left = 5;
      int top = 5;
      int width = window->width - 10;
      int height = window->height;
      if(JS_IsString(argv[0])) {
        char *str = (char*)JS_ToCString(ctx, argv[0]);
        if (str) {
          FILE* s = window->getStream(left, top, width, height);
          fprintf(s, str);
        }
        JS_FreeCString(ctx, str);
      }else{

      }
    }
      
    return JS_UNDEFINED;
  }
  
  static JSValue js_window_refresh(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
    Window *window = (Window*)JS_GetOpaque(this_val);
    window->needRefresh = 1;
    return JS_UNDEFINED;
  }
  
  // static JSValue js_window_render(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
  //     Window *p = (Window*)JS_GetOpaque(this_val);
  //     if(p){
  //       p->render();
  //     }
  //     return JS_UNDEFINED;
  // }
  // 函数render,内部自行调用基类函数，并且触发相应事件  事件（特殊属性） beforeRender afterRender onClick 等 ， 属性 pos
  // 区分完整渲染和部分渲染，完整渲染切换缓冲区

  void window_init(JSValue* window_ctor, JSValue* window_proto) {
    JS_NewClassID(&js_window_class_id);
    JSClassDef window_def = {
      .class_name = "Window",
      .finalizer = js_window_finalizer,
    };
    JS_NewClass(rt, js_window_class_id, &window_def);

    *window_proto = JS_NewObject(ctx);

${window_fields.map((field)=>{
  return regFieldGetSet("window", "*window_proto", field);
}).join("")}

${window_event.map((event)=>{
  return regEvent("window", "*window_proto", event);
}).join("")}

    JS_SetClassProto(ctx, js_window_class_id, *window_proto);

    JS_SetPropertyStr(ctx, *window_proto, "paint", JS_NewCFunction(ctx, js_window_paint, "paint", 5));
    JS_SetPropertyStr(ctx, *window_proto, "print", JS_NewCFunction(ctx, js_window_print, "print", 1));
    JS_SetPropertyStr(ctx, *window_proto, "refresh", JS_NewCFunction(ctx, js_window_refresh, "refresh", 0));

    *window_ctor = JS_NewCFunction2(ctx, js_window_ctor, "Window", 2, JS_CFUNC_constructor, 0);
    JS_SetConstructor(ctx, *window_ctor, *window_proto);
    JS_SetPropertyStr(ctx, global, "Window", *window_ctor);
  }
`;

module.exports = { window, window_def };



//     if(argc>=1 && JS_IsObject(argv[0])) {
//       JSValue val = argv[0];

//${window_fields.map((field)=>/* cpp */`
//       getIntVal(val, "${field}", &(p->${field}));
// `).join("")}

//     }
